package com.gdc.miaosha.controller;


import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gdc.miaosha.config.AccessLimit;
import com.gdc.miaosha.config.UserContext;
import com.gdc.miaosha.constants.RedisConstant;
import com.gdc.miaosha.entity.MiaoshaGoods;
import com.gdc.miaosha.entity.OrderInfo;
import com.gdc.miaosha.entity.User;
import com.gdc.miaosha.result.CodeMsg;
import com.gdc.miaosha.result.Result;
import com.gdc.miaosha.service.*;
import com.gdc.miaosha.util.RedisUtil;
import com.gdc.miaosha.vo.GoodsVO;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Description: 秒杀 接口
 * @author: gdc
 * @date: 2022/4/5
 * @version 1.0
 *
 *
 * InitializingBean接口，只包括afterPropertiesSet方法，凡是继承该接口的类，在初始化bean的时候都会执行该方法。
 */
@Slf4j
@Api(value = "秒杀接口")
@RestController
@RequestMapping("/miaosha")
public class MiaoshaController implements InitializingBean {

    /**
     * 设置本地缓存变量，本意是从本地取值，减少Redis访问
     */
    private Map<Integer, Boolean> localOverMap = Maps.newHashMap();

    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private IMiaoshaGoodsService miaoshaGoodsService;
    @Autowired
    private IOrderInfoService orderInfoService;
    @Autowired
    private IMiaoshaOrderService miaoshaOrderService;
    @Autowired
    private IMiaoShaService miaoshaService;

    /**
     * 订单秒杀接口
     *  网页静态化时，弃用
     */
    /*@RequestMapping("/do_miaosha2")
    @ApiOperation(value = "秒杀操作")
    public String miaosha2(Model model, User user,
                       @RequestParam("goodsId") Integer goodsId) {
        // 用户未登录，跳转到登陆页面
        if(Objects.isNull(user)) {
            return "login";
        }
        model.addAttribute("user", user);

        //判断商品库存，库存不够跳转到秒杀失败页面
        GoodsVO goods = goodsService.getGoodsVoById(goodsId);
        if (Objects.isNull(goods) || goods.getStockCount() <= 0) {
            model.addAttribute("errmsg", CodeMsg.MIAO_SHA_OVER.getMsg());
            return "miaosha_fail";
        }

        // 获取秒杀订单数
        Integer orderNum = miaoshaOrderService.countByUserIdAndGoodsId(user.getId(), goodsId);
        if(orderNum > 0) {
            model.addAttribute("errmsg", CodeMsg.MIAO_SHA_REPEATE.getMsg());
            return "miaosha_fail";
        }

        OrderInfo order = miaoshaService.createMiaoShaOrder(user, goods);
        model.addAttribute("orderInfo", order);
        model.addAttribute("goods", goods);
        return "order_detail";
    }*/

    /**
     * 订单秒杀接口， 使用了 网页静态化 处理
     *  接口优化时弃用（缓存、队列等）
     */
    /*@PostMapping("/do_miaosha3")
    @ResponseBody
    public Result<OrderInfo> miaosha3(User user, @RequestParam("goodsId") Integer goodsId) {
        // 用户未登录，跳转到登陆页面
        if(Objects.isNull(user)) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        // 判断商品库存，库存不够跳转到秒杀失败页面
        GoodsVO goods = goodsService.getGoodsVoById(goodsId);
        if (Objects.isNull(goods) || goods.getStockCount() <= 0) {
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }

        // 获取秒杀订单数
        Integer orderNum = miaoshaOrderService.countByUserIdAndGoodsId(user.getId(), goodsId);
        if(orderNum > 0) {
            return Result.error(CodeMsg.MIAO_SHA_REPEATE);
        }

        // 创建秒杀订单
        OrderInfo order = miaoshaService.createMiaoShaOrder(user, goods);
        return Result.success(order);
    }*/

    /**
     * 订单秒杀接口
     *  使用缓存库存代替数据库查询
     */
    @PostMapping("/{path}/do_miaosha")
    public Result<OrderInfo> miaosha(@RequestParam("goodsId") Integer goodsId,
                                     @PathVariable("path") String path) {
        // 用户未登录，跳转到登陆页面
        User user = UserContext.getUser();
        if(Objects.isNull(user)) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        // 验证path
        boolean check = miaoshaService.checkPath(user.getId(), goodsId, path);
        if(!check){
            return Result.error(CodeMsg.REQUEST_ILLEGAL);
        }

        // 内存标记，减少redis访问
        if(localOverMap.get(goodsId)) {
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }

        // 通过Redis, 进行预减库存
        long stock = RedisUtil.decr(RedisConstant.MIAOSHA_GOODS_STOCK + goodsId, 1);
        if(stock < 0) {
            localOverMap.put(goodsId, true);
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }

        // 判断商品库存，库存不够跳转到秒杀失败页面
        GoodsVO goods = goodsService.getGoodsVoById(goodsId);
        if (Objects.isNull(goods) || goods.getStockCount() <= 0) {
            return Result.error(CodeMsg.MIAO_SHA_OVER);
        }

        // 获取秒杀订单数
        Integer orderNum = miaoshaOrderService.countByUserIdAndGoodsId(user.getId(), goodsId);
        if(orderNum > 0) {
            return Result.error(CodeMsg.MIAO_SHA_REPEATE);
        }

        // 创建秒杀订单
        OrderInfo order = miaoshaService.createMiaoShaOrder(user, goods);
        return Result.success(order);
    }

    @GetMapping("/result")
    @ApiOperation(value = "获取秒杀结果")
    public Result<Integer> miaoshaResult(@RequestParam("goodsId") Integer goodsId) {
        User user = UserContext.getUser();
        if (Objects.isNull(user)) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        return Result.success(miaoshaService.getResult(user.getId(), goodsId));
    }

    @AccessLimit(seconds = 5, maxCount = 5)
    @GetMapping("/path")
    @ApiOperation(value = "获取秒杀地址")
    public Result<String> getMiaoshaPath(@RequestParam("goodsId") Integer goodsId, Integer verifyCode) {
        User user = UserContext.getUser();
        if (Objects.isNull(user)) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        if (Objects.isNull(verifyCode)) {
            return Result.error(CodeMsg.VERIFYCODE_NOT_EXIST);
        }
        // 验证验证码的值
        if (!miaoshaService.checkVerifyCode(user.getId(), goodsId, verifyCode)) {
            return Result.error(CodeMsg.VERIFYCODE_ERROR);
        }
        return Result.success(miaoshaService.createMiaoShaPath(user.getId(), goodsId));
    }

    @GetMapping("/verifyCode")
    @ApiOperation(value = "获取秒杀验证码")
    public Result getMiaoshaVerifyCode(HttpServletResponse response, @RequestParam("goodsId") Integer goodsId) {
        User user = UserContext.getUser();
        if (Objects.isNull(user)) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }
        try (OutputStream out = response.getOutputStream()) {
            BufferedImage image = miaoshaService.createVerifyCode(user.getId(), goodsId);
            ImageIO.write(image, "JPEG", out);
            out.flush();
            return Result.success();
        } catch (Exception e) {
            log.error("获取验证码接口出现异常，异常的原因为：{}", Throwables.getStackTraceAsString(e));
            return Result.error(CodeMsg.MIAOSHA_FAIL);
        }
    }

    @GetMapping("/reset")
    @ApiOperation(value = "重置秒杀商品库存、订单")
    public Result reset() {
        List<GoodsVO> goodsList = goodsService.listGoodsVO();
        if (CollectionUtil.isEmpty(goodsList)) {
            return Result.success();
        }

        // 商品库存数
        final int GOODS_STOCK_NUM = 10;
        for(GoodsVO goods : goodsList) {
            // 更新秒杀商品库存
            RedisUtil.set(RedisConstant.MIAOSHA_GOODS_STOCK + goods.getId(), GOODS_STOCK_NUM);
            // 更新商品抢完标识为false
            localOverMap.put(goods.getId(), false);
            // 更新秒杀商品库存为10
            miaoshaGoodsService.lambdaUpdate().set(MiaoshaGoods::getStockCount, GOODS_STOCK_NUM).eq(MiaoshaGoods::getGoodsId, goods.getId()).update();
        }

        // 删除所有的秒杀订单
        orderInfoService.remove(new QueryWrapper<>());
        // 删除所有的秒杀订单
        miaoshaOrderService.remove(new QueryWrapper<>());

        // 清除秒杀订单缓存
        RedisUtil.delPrefix(RedisConstant.MIAOSHA_ORDER_USER_GOODS + "*");
        // 清除秒杀商品是否抢完缓存
        RedisUtil.delPrefix(RedisConstant.MIAOSHA_GOODS_STOCK_OVER + "*");

        return Result.success();
    }

    /**
     * 在初始化bean的时候会执行该方法，将所有秒杀商品的库存添加到Redis中
     */
    @Override
    public void afterPropertiesSet() {
        goodsService.listGoodsVO().forEach(goods -> {
            RedisUtil.set(RedisConstant.MIAOSHA_GOODS_STOCK + goods.getId(), goods.getStockCount());
            // 存放商品使用标识
            localOverMap.put(goods.getId(), false);
        });
    }
}
